package svrConnectMgr

import (
	"crypto/rand"
	redisgw "fronter/redisGw"
	"math/big"
	"net"
	"reflect"
	"sync"
	"time"

	"github.com/lesismal/arpc"
	"github.com/sirupsen/logrus"
)

type myClient struct {
	svrName string
	addr    string
	client  *arpc.Client
}

//naming->address->arpc.Client的映射
var rwlock *sync.RWMutex
var svrPool map[string]map[string]myClient

var myerr error //通用错误返回

func init() {
	rwlock = new(sync.RWMutex)
	svrPool = make(map[string]map[string]myClient)

}

func CallWithRetry(serverName string, path string, req interface{}, rsp *interface{}, timeout time.Duration, retryTime int, args ...interface{}) error {
	for i := 0; i < retryTime; i++ {
		if Call(serverName, path, req, rsp, timeout, args) == nil {
			return nil
		}
	}
	logrus.Errorln("call server:", serverName, " failed in :", retryTime, ", please check!")
	return myerr
}

func Call(serverName string, path string, req interface{}, rsp *interface{}, timeout time.Duration, args ...interface{}) error {
	address, svrWithId := redisgw.Svr(serverName)
	logrus.Infoln("svr:", serverName, ", addr:", address)
	rwlock.Lock()
	defer rwlock.Unlock()
	if svrPool[serverName] == nil {
		svrPool[serverName] = make(map[string]myClient)
	}
	clientIns, clientok := svrPool[serverName][address]
	if clientok {
		//直接调用
		callErr := clientIns.callImpl(path, req, rsp, args)
		return callErr
	} else {
		//建立连接
		logrus.Infoln("no connect to:", serverName, ", try to new one.")
		tmpClient, tcpErr := arpc.NewClient(func() (net.Conn, error) {
			return net.DialTimeout("tcp", address, timeout)
		})
		logrus.Infoln("@@@@@@@@@@:", tcpErr)
		if tcpErr != nil {
			//连接错误，目标服务器出现网络分区，则使用已有连接
			logrus.Infoln("tcperr:", tcpErr)
			//清除hset中的此服务
			redisgw.TryToClearDeadSvr(serverName, svrWithId, address)

			logrus.Warningln("svr:", address, " connected failed! try to use local connect.")
			if len(svrPool[serverName]) == 0 {
				logrus.Errorln("connect failed! ", serverName, " has no instance! tcp err:", tcpErr)
				return myerr
			} else {
				//随机选择已有server
				idx, _ := rand.Int(rand.Reader, big.NewInt(int64(len(svrPool[serverName]))))
				var i int = 0
				for addr, instance := range svrPool[serverName] {
					if i == int(idx.Int64()) {
						callErr := instance.callImpl(path, req, rsp, args)
						logrus.Infoln("send to ", serverName, ":", addr)
						return callErr
					}
					i++
				}
				return myerr
			}
		} else {
			//设置断开连接的回调：清除连接池中的对象
			tmpClient.Handler.HandleDisconnected(func(c *arpc.Client) {
				rwlock.Lock()
				defer rwlock.Unlock()
				logrus.Infoln("svr:", serverName, " disconnect. remote addr:", c.Conn.RemoteAddr().String())
				_, ok := svrPool[serverName][c.Conn.RemoteAddr().String()]
				if ok {
					delete(svrPool[serverName], c.Conn.RemoteAddr().String())
				}
			})
			svrPool[serverName][address] = myClient{
				svrName: serverName,
				addr:    address,
				client:  tmpClient,
			}
			//然后调用
			clientIns = svrPool[serverName][address]
			callErr := clientIns.callImpl(path, req, rsp, args)
			return callErr
		}

	}

}

func (p *myClient) callImpl(path string, req interface{}, rsp *interface{}, args ...interface{}) error {
	logrus.Infoln("rpc to :", p.svrName, ", ip:", p.addr)
	err := p.client.Call(path, &req, rsp, time.Second*5)
	if err != nil {
		logrus.Errorln("Call failed:", err)
		return err
	} else {
		logrus.Println("Call Response:", rsp, ", typeof:", reflect.TypeOf(*rsp))
		return nil
	}
}
